//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------

using System;
using System.Xml;
using System.IO;
using System.Text;

using System.Configuration;
using System.Collections.Specialized;
using System.IO.Compression;

using Microsoft.IdentityServer.Web;
using Microsoft.IdentityServer.Web.UI;

/// <summary>
/// Attempts to authenticate the user via HTTP Forms Authentication.
/// </summary>
public partial class FormsSignIn : FormsLoginPage
{
    protected void Page_Load( object sender, EventArgs e )
    {
        string rpIdentity = GetRelyingPartnerUrl();

        if (!String.IsNullOrEmpty(rpIdentity))
        {
            // Is the replying partner is a service provider who's authentication method should be FBA
            Uri rpUri;
            if (Uri.TryCreate(rpIdentity, UriKind.Absolute, out rpUri))
            {
                string rpHost = rpUri.Host;

                NameValueCollection appSettings = ConfigurationManager.AppSettings;
                string fbaRelyingPartners = appSettings["fbaRelyingPartners"];
                if (!String.IsNullOrEmpty(fbaRelyingPartners))
                {
                    foreach (string fbaRelyingPartner in fbaRelyingPartners.Split(','))
                    {
                        if (fbaRelyingPartner.Equals(rpHost, StringComparison.InvariantCultureIgnoreCase))
                        {
                            return;
                        }
                    }
                }
            }
        }

        // Else redirect for IWA
        Response.Redirect("/adfs/ls/auth/integrated/" + Request.Url.Query);
    }

    protected void HandleError( string message )
    {
        ErrorTextLabel.Visible = true;
        ErrorTextLabel.Text = Resources.CommonResources.IncorrectUsernameText;
    }

    protected void SubmitButton_Click( object sender, EventArgs e )
    {
        try
        {
            if (!UsernameTextBox.Text.Contains(@"\"))
            {
                UsernameTextBox.Text = @"EUROPE\" + UsernameTextBox.Text;
            }
            SignIn( UsernameTextBox.Text, PasswordTextBox.Text );
        }
        catch ( AuthenticationFailedException ex )
        {
            HandleError( ex.Message );
        }
    }

    private string GetRelyingPartnerUrl()
    {
        // If using WS-Federation, get issuer from wtrealm query parameter
        NameValueCollection queryParams = System.Web.HttpUtility.ParseQueryString(Request.Url.Query);
        string wtrealm = queryParams["wtrealm"];
        if (!String.IsNullOrEmpty(wtrealm))
        {
            return wtrealm;
        }

        // Next try to get SAML Request for the URL (i.e. user is using IdP Initiated SSO or 
        // SAML SSO Redirect binding).  The SAML Request is Base64 encoded and compressed
        string decodedSamlRequest;
        string urlSamlRequestBase64 = queryParams["SAMLRequest"];

        if (!String.IsNullOrEmpty(urlSamlRequestBase64))
        {
            // convert from Base64
            byte[] encodedCompressedDataAsBytes = Convert.FromBase64String(urlSamlRequestBase64);
            using (MemoryStream inputBuffer = new MemoryStream())
            {
                // Read the data via a deflate stream input an output buffer
                using (MemoryStream outputBuffer = new MemoryStream(encodedCompressedDataAsBytes))
                {
                    using (DeflateStream inflateStream = new DeflateStream(outputBuffer, CompressionMode.Decompress, false))
                    {
                        // Should really be able to use inflateStream.CopyTo(inputBuffer) but it is throwing an exception
                        int c;
                        while ((c = inflateStream.ReadByte()) >= 0)
                        {
                            inputBuffer.WriteByte((byte)c);
                        }
                    }
                }
                // Write the output (XML) to a string
                decodedSamlRequest = Encoding.UTF8.GetString(inputBuffer.ToArray());
            }
        }
        // Next try to get SAML Request from FORMS (i.e. user is using SAML SSO POST Binding)
        // It's Base64 encoded, and ADFS embeddes it in MSISSamlRequest
        else
        {
            // Get Base54 encoded MSI SAML Request from Request parameters
            string msiSamlRequestParam = Request.Params["MSISSamlRequest"];
            if (String.IsNullOrEmpty(msiSamlRequestParam))
            {
                return null;
            }
            string[] msiSamlRequestList = msiSamlRequestParam.Split(',');
            string msiSamlRequestBase64 = msiSamlRequestList[msiSamlRequestList.Length - 1];

            // Decode MSI SAML Request parameter
            byte[] encodedDataAsBytes = System.Convert.FromBase64String(msiSamlRequestBase64);
            string msiSamlRequest = Encoding.UTF8.GetString(encodedDataAsBytes);

            // Get Base64 encoded SAML Request from the decoded MSI SAML Request
            int startIndex = msiSamlRequest.IndexOf("SAMLRequest=") + 12;
            if (startIndex == -1)
            {
                return null;
            }
            string samlRequestBase64;
            int endIndex = msiSamlRequest.IndexOf("\\", startIndex);
            if (endIndex == -1)
            {
                samlRequestBase64 = msiSamlRequest.Substring(startIndex);
            }
            else
            {
                samlRequestBase64 = msiSamlRequest.Substring(startIndex, endIndex - startIndex);
            }
            samlRequestBase64 = samlRequestBase64.Replace("%3d", "=").Replace("%2b", "+"); //TODO better way?

            // Decode SAML Request parameter
            encodedDataAsBytes = System.Convert.FromBase64String(samlRequestBase64);
            decodedSamlRequest = Encoding.UTF8.GetString(encodedDataAsBytes);
        }

        // Failed to get a SAML Request - Should not happen
        if (String.IsNullOrEmpty(decodedSamlRequest))
        {
            return null;
        }

        // Get the URL of service provider from the SAML Request
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(decodedSamlRequest);

        // Look at Audience, incase the issuer is ADFS
        XmlNodeList audienceList = doc.GetElementsByTagName("Audience","urn:oasis:names:tc:SAML:2.0:assertion");
        if ((audienceList.Count > 0) &&
            (audienceList[0] is XmlElement))
        {
            return ((XmlElement)audienceList[0]).InnerText;
        }

        // If Audience is not used, look at Issuer
        XmlNodeList issuerList = doc.GetElementsByTagName("Issuer","urn:oasis:names:tc:SAML:2.0:assertion");
        if ((issuerList.Count > 0) &&
            (issuerList[0] is XmlElement))
        {
            return ((XmlElement)issuerList[0]).InnerText;
        }

        return null;
    }
}
